use std::iter::Peekable;
use std::str::Chars;

use super::token::Token;

pub struct Lexer<'a> {
   input: Peekable<Chars<'a>>,
   reached_end: bool,             // 是否结束
   unexpected_char: Option<char>, // 未知字符
}

impl<'a> Lexer<'a> {
   pub fn new(input: &'a str) -> Self {
      Self {
         input: input.chars().peekable(),
         reached_end: false,
         unexpected_char: None,
      }
   }

   pub fn get_unexpected_char(&self) -> Option<char> {
      self.unexpected_char
   }
}

impl<'a> Iterator for Lexer<'a> {
   type Item = Token;

   fn next(&mut self) -> Option<Self::Item> {
      if self.reached_end {
         // 结束
         return None;
      }

      let next_char = self.input.next();
      match next_char {
         Some(ch) if ch.is_numeric() => {
            let mut number_str = String::from(ch);
            // while let Some(next) = self.input.peek() {
            //    if next.is_numeric() {
            //       number_str.push(self.input.next().unwrap());
            //    } else {
            //       break;
            //    }
            // }

            while let Some(next) = self.input.next_if(|c| c.is_numeric()) {
               number_str.push(next);
            }

            Some(Token::Number(number_str.parse().unwrap()))
         }
         Some(ch) if ch.is_whitespace() => {
            while self.input.next_if(|c| c.is_whitespace()).is_some() {
               println!("is whitespace");
            }
            self.next()
         }
         Some('+') => Some(Token::Add),
         Some('-') => Some(Token::Subtract),
         Some('*') => Some(Token::Multiply),
         Some('/') => Some(Token::Divide),
         Some('^') => Some(Token::Caret),
         Some('(') => Some(Token::LeftParen),
         Some(')') => Some(Token::RightParen),
         Some(ch) => {
            self.unexpected_char = Some(ch);
            None
         }
         None => {
            self.reached_end = true;
            Some(Token::Eof)
         }
      }
   }
}

#[cfg(test)]
mod tests {
   use rust_decimal::dec;

   use super::*;

   #[test]
   fn test_tokenizer() {
      let input = "1    + 2 * 3   ";
      let lexer = Lexer::new(input);
      assert_eq!(
         lexer.collect::<Vec<Token>>(),
         vec![
            Token::Number(dec!(1)),
            Token::Add,
            Token::Number(dec!(2)),
            Token::Multiply,
            Token::Number(dec!(3)),
            Token::Eof
         ]
      );
   }
}
